from event import Goal, EndOfGame, YellowCard, RedCard
from matchteam import CARD_RED, CARD_YELLOW
from broadcaster import Broadcaster

from random import random, randint
from math import sin, cos


class Watch(object):
     
    DURATION_IN_MINUTES = 45
    TICKS_PER_MINUTE = 60
    
    def __init__(self):
        self.finished = False
        self.__time = 0
        self.half = 1
        
    def update(self):
        if not self.finished:
            self.__time += 1
            if self.minutes >= Watch.DURATION_IN_MINUTES:
                if self.half >= 2:
                    self.finished = True
                else:
                    self.half += 1
                    self.__time = 0

    def get_minutes(self):
        return self.__time / Watch.TICKS_PER_MINUTE

    minutes = property(get_minutes)

    def get_total_minutes(self):
        return (self.minutes
                + (Watch.DURATION_IN_MINUTES if self.half == 2 else 0))

    total_minutes = property(get_total_minutes)
    

class Ingame(Broadcaster):
    
    def __init__(self, home, away):
        Broadcaster.__init__(self)
        self.match_teams = (home, away)
        self.finished = False
        self.watch = Watch()
        self.ball = Ball(self)
        self.reposition()

    def reposition(self):
        self.ball.reposition()
        self.match_teams[0].reposition()
        self.match_teams[1].reposition()

    def update(self):
        if not self.finished:
            self.watch.update()
            if self.watch.finished:
                event = EndOfGame()
                self.broadcast_event(event)
                self.finished = True
            else:
                self.step()
                self.__goal_chance()
                self.__card_chance()

    def step(self):
        for mt in self.match_teams:
            #if not mt.home:
                #continue
            for mp in mt.match_players:
                mp.step(self.ball)
        self.ball.step()

    def __goal_chance(self):
        if random() < 0.04 / Watch.TICKS_PER_MINUTE:
            t = randint(0, 1)
            p = randint(1, 10)
            match_team = self.match_teams[t]
            match_player = match_team.match_players[p]
            
            if not match_player.is_sent_off():
                match_team.score_goal()
                match_player.score_goal()
                
                event = Goal(match_team, match_player, self.watch.total_minutes)
                print event
                self.broadcast_event(event)

    def __card_chance(self):
        if random() < 0.06 / Watch.TICKS_PER_MINUTE:
            card = YellowCard if random() < 0.90 else RedCard
            t = randint(0, 1)
            p = randint(0, 10)
            match_team = self.match_teams[t]
            match_player = match_team.match_players[p]
            
            if not match_player.is_sent_off():
                match_player.show_card(CARD_YELLOW if card is YellowCard else CARD_RED)
                
                event = card(match_team, match_player, self.watch.total_minutes)
                print event
                self.broadcast_event(event) 


class Ball(object):
    
    def __init__(self, ingame):
        self.position = [0.5, 0, 0.5]
        self.ingame = ingame
        self.controller = None
        self.speed = [0, 0, 0]

    def reposition(self):
        self.position = [0.5, 0, 0.5]
    
    def take_possession(self, player):
        if self.controller is None:
            print '%s takes possession of the ball' % player
            self.controller = player
            return True
        else:
            return False

    def kick(self, kicker, power, angle, y_angle):
        """
        kicker should be a MatchPlayer, power a float number, angle a 
        [0, 2 pi] number, starting right, going clockwise, y_angle a
        [0, pi / 2] number.
        """
        print 'ball.kick(%s, %f, %f, %f)' % (kicker, power, angle, y_angle)
        self.speed[0] = power * cos(y_angle) * cos(angle)
        self.speed[1] = power * sin(y_angle)
        self.speed[2] = power * cos(y_angle) * sin(angle)
        self.controller = None
        
    def step(self):
        self.__apply_gravity()
        self.__apply_friction()
        self.position[0] += self.speed[0]
        self.position[1] += self.speed[1]
        self.position[2] += self.speed[2]
        self.__keep_inside_boundaries()

    def __apply_gravity(self):
        self.speed[1] -= 0.01

    def __apply_friction(self):
        self.speed[0] *= 0.99
        self.speed[2] *= 0.99

    def __keep_inside_boundaries(self):
        if self.position[1] < 0:
            self.position[1] = 0
            
        if self.position[0] < 0:
            self.position[0] = 0
            self.speed[0] *= -1
        elif self.position[0] > 1:
            self.position[0] = 1
            self.speed[0] *= -1
            
        if self.position[2] < 0:
            self.position[2] = 0
            self.speed[2] *= -1
        elif self.position[2] > 1:
            self.position[2] = 1
            self.speed[2] *= -1

    def get_xz_position(self):
        return (self.position[0], self.position[2])
